package nachos.threads;

import java.util.LinkedList;

import nachos.machine.Lib;

/**
 * A <i>communicator</i> allows threads to synchronously exchange 32-bit
 * messages. Multiple threads can be waiting to <i>speak</i>,
 * and multiple threads can be waiting to <i>listen</i>. But there should never
 * be a time when both a speaker and a listener are waiting, because the two
 * threads can be paired off at this point.
 */
public class Communicator {
    /**
     * Allocate a new communicator.
     */
    public Communicator() {
		communicatorLock = new Lock();
		speakercondition = new Condition(communicatorLock);
		listenercondition = new Condition(communicatorLock);    
		speakerQueue = new LinkedList<KThread>();
		listenerQueue = new LinkedList<KThread>();
		message = null;
    	
    }

    /**
     * Wait for a thread to listen through this communicator, and then transfer
     * <i>word</i> to the listener.
     *
     * <p>
     * Does not return until this thread is paired up with a listening thread.
     * Exactly one listener should receive <i>word</i>.
     *
     * @param	word	the integer to transfer.
     */
    public void speak(int word) {
    	communicatorLock.acquire();
    	if(listenerQueue.isEmpty())
    	{
    		speakerQueue.offer(KThread.currentThread());
    		speakercondition.sleep();
    		
    	}
    	else{
    		listenercondition.wake();
    		listenerQueue.poll();
    		System.out.println(KThread.currentThread() + " speaks "+ word);
        	message = word;
        	communicatorLock.release();
        	return;
    	}
    	System.out.println(KThread.currentThread() + " speaks "+ word);
    	message = word;
    	listenercondition.wake();
    	//speakerQueue.poll();
    	communicatorLock.release();
    	return;
    }
    //boolean has
    /**
     * Wait for a thread to speak through this communicator, and then return
     * the <i>word</i> that thread passed to <tt>speak()</tt>.
     *
     * @return	the integer transferred.
     */    
    public int listen() {
    	communicatorLock.acquire();
    	if(speakerQueue.isEmpty())
    	{
    		listenerQueue.offer(KThread.currentThread());
    		listenercondition.sleep();
    		int tempmessage = message;
        	message = null;
        	communicatorLock.release();
        	//listenerQueue.poll();
        	//test
        	return tempmessage;
    	}
    	else{
    		speakercondition.wake();
    		speakerQueue.poll();
    		listenercondition.sleep();
    		Lib.assertTrue(message!=null);	
        	int tempmessage = message;
        	message = null;
        	communicatorLock.release();
        	//listenerQueue.poll();
        	return tempmessage;
    	}
    
    }

    private Lock communicatorLock; 
    private Condition speakercondition;
    private Condition listenercondition; 
    private LinkedList<KThread> speakerQueue;
    private LinkedList<KThread> listenerQueue;
    private Integer message;
}
